#region Copyright Notices
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Library General Public License for more details.
// 
// You should have received a copy of the GNU Library General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#endregion

using System;
using System.IO;
using System.Collections.Generic;
using Yarr;

namespace Yarr.CommandConsole
{
	/// <summary>
	/// Provides a command line console for Yarr
	/// </summary>
	class Program
	{
        private const string INIT_FILE = "yarr-init.lsp";

        /// <summary>
        /// Displays an introductory banner message
        /// </summary>
		private static void Banner() 
		{
            Console.SetWindowSize(96, 50);
            Console.BackgroundColor = ConsoleColor.White;
            Console.ForegroundColor = ConsoleColor.Black;
            Console.Title = "Yarr Console";

            Console.Clear();

            Console.WriteLine("\nWelcome to Yarr\n");
			Console.WriteLine("This program is free software and is distributed");
			Console.WriteLine("under the terms of the GNU General Public License.\n");
		}

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
	    /// [STAThread]
		static void Main(string[] args)
		{
            Environment environment = Environment.MakeEnvironment();

            // Put the command-line args into a stack in the Yarr environment
            Stack<string> cmdLine = null;
            if (args.Length > 0)
            {
                cmdLine = new Stack<string>(args.Length);
                for (int ix = args.Length - 1; ix >= 0; --ix)
                    cmdLine.Push(args[ix]);
            }

            environment["*command-line-args*"] = cmdLine;
            ConsoleDebugger debugger = null;

            // Load system init.lisp file
            string initFile = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, INIT_FILE);
            if (File.Exists(initFile))
            {
                try
                {
                    Runtime.Load(initFile, null, environment);
                }
                catch (Exception ex)
                {
                    Console.Error.WriteLine("Error Loading Init file: {0}", ex.Message);
                }
            }

            bool interactive = args.Length < 1;
            bool silent = !interactive;
            bool verbose = false;
            bool debuggerActive = false;
            string pathName;

            // Load files specified in command-line
            while (cmdLine != null  &&  cmdLine.Count > 0)
            {
                string arg = cmdLine.Pop();

                try
                {
                    switch (arg)
                    {
                        case "-h":
                        case "-?":
                            PrintHelp();
                            break;

                        // Set interactive mode
                        case "-i":
                            interactive = true;
                            silent = false;
                            break;

                        // Set debugger active
                        case "-d":
                            if (args.Length < 2)
                            {
                                interactive = true;
                                silent = false;
                            }
                            debuggerActive = true;
                            debugger = new ConsoleDebugger();
                            Runtime.AttachDebugger(debugger);
                            Runtime.EnableDebugger(DebuggerMode.RunToBreakpoint);
                            break;

                        // Set silent mode
                        case "-s":
                            interactive = true;
                            verbose = false;
                            silent = true;
                            break;

                        // Set verbose mode
                        case "-v":
                            verbose = true;
                            break;

                        // Reference an assembly
                        case "-r":
                            if (cmdLine.Count > 0)
                            {
                                arg = cmdLine.Pop();
                                object o = Runtime.LoadAssembly(arg);
                                if (verbose)
                                    Console.WriteLine("Referencing: {0}", o);
                            }
                            break;

                        // execute a yarr expression
                        case "-x":
                            if (cmdLine.Count > 0)
                            {
                                arg = cmdLine.Pop();
                                object o = Runtime.EvalString(arg, environment);
                                if (o != null  &&  o is Cons)
                                    o = ((Cons)o).Cdr();
                                Console.WriteLine(":= {0}", o);
                            }
                            break;

                        // Try to load a lisp file or import a dll
                        default:
                            pathName = Path.GetFullPath(arg);
                            if (File.Exists(pathName))
                            {
                                object o = Runtime.Load(pathName, null, environment);

                                if (verbose)
                                    Console.WriteLine("Loaded: {0}", o);
                            }
                            else
                                Console.WriteLine("Warning: File \"{0}\" not found", pathName);
                            break;
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }

            if (!silent)
            {
                Banner();
                Console.WriteLine("Build: {0}\nOS: {1}\nCLR: {2}\n",
                                    System.Reflection.Assembly.GetExecutingAssembly().GetName().Version,
                                    System.Environment.OSVersion,
                                    System.Environment.Version);
            }

            if (interactive) 
			{
                if (debugger == null)
                    debugger = new ConsoleDebugger();
                TopLoop.MakeRepl(environment).Run(debugger, debuggerActive);
			} 
		}

        private static void PrintHelp ()
        {
            Console.WriteLine("\nusage: yarr [-i] [-s] [-v] [-r assembly]* [-x expression]* [filename]*");
            Console.WriteLine("    -i      Interactive mode");
            Console.WriteLine("    -s      Silent mode");
            Console.WriteLine("    -v      Verbose mode");
            Console.WriteLine("    -d      Debug mode: causes debugger to always be active");
            Console.WriteLine("    -r      Reference an assembly (DLL or assembly name)");
            Console.WriteLine("    -x      Evaluate an expression");
            Console.WriteLine(" filename   Load (lisp) or Import (.DLL) file\n");
        }
	}
}
